#pragma once

#include <vector>

namespace observer {

/// @brief 抽象观察者, 包含了一个更新自己的抽象方法，当接到具体主题的更改通知时被调用。
class AbstractObserver {
public:
    ~AbstractObserver() {}

    // 被告知时做出响应
    virtual void Response() = 0;
};

/// @brief 具体观察者, 实现抽象观察者中定义的抽象方法，以便在得到目标的更改通知时更新自身的状态。
class ConcreteObserverA : public AbstractObserver {
public:
    virtual void Response() {
        printf("this is ConcreteObserverA::Response!\n");
    }
};
class ConcreteObserverB : public AbstractObserver {
public:
    virtual void Response() {
        printf("this is ConcreteObserverB::Response!\n");
    }
};

/// @brief 抽象主题类, 它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法，以及通知所有观察者的抽象方法
class AbstractSubject {
public:
    virtual ~AbstractSubject() {}

    virtual void NotifyObserver() = 0;

    void AddObserver(AbstractObserver* observer) {
        observers_.emplace_back(observer);
    }
    void RemoveObserver(AbstractObserver* observer) {
        for (auto it = observers_.begin(); it != observers_.end(); it++) {
            if ((*it) == observer) {
                observers_.erase(it++);
                break;
            }
        }
    }

protected:
    std::vector<AbstractObserver*> observers_;
};

/// @brief 具体主题类, 它实现抽象目标中的通知方法，当具体主题的内部状态发生改变时，通知所有注册过的观察者对象。
class ConcreteSubject : public AbstractSubject {
public:
    virtual void NotifyObserver() {
        for (auto* observer : observers_) {
            observer->Response();
        }
    }
};
    
} // namespace observer

void Test_18_observer_impl_1() {
    printf("-------------------- %s --------------------\n", __FUNCTION__);
    std::shared_ptr<observer::AbstractSubject> ptr_subject(new observer::ConcreteSubject());
    std::shared_ptr<observer::AbstractObserver> ptr_observer_A(new observer::ConcreteObserverA());
    std::shared_ptr<observer::AbstractObserver> ptr_observer_B(new observer::ConcreteObserverB());

    ptr_subject->AddObserver(ptr_observer_A.get());
    ptr_subject->AddObserver(ptr_observer_B.get());

    ptr_subject->NotifyObserver();
}

// -------------------- Test_18_observer_impl_1 --------------------
// this is ConcreteObserverA::Response!
// this is ConcreteObserverB::Response!